﻿//  Symplectic Spark
//  Copyright 2010 Symplectic Ltd
//  Created by Martyn Whitwell (martyn@symplectic.co.uk)

//  This file is part of Spark.

//  Spark is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.

//  Spark is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with Spark.  If not, see <http://www.gnu.org/licenses/>.

// $URL: https://symplectic-spark.googlecode.com/svn/trunk/website/App_Code/DynamicElementInvoker.cs $
// $LastChangedDate: 2010-03-02 15:38:13 +0000 (Tue, 02 Mar 2010) $
// $LastChangedRevision: 11 $
// $LastChangedBy: martyn@symplectic.co.uk $

using System;
using System.Reflection;
using System.CodeDom.Compiler;


//Based on http://www.west-wind.com/presentations/DynamicCode/DynamicCode.htm
//Also http://www.codeproject.com/KB/dotnet/DynamicCompileAndRun.aspx?msg=771338

namespace Symplectic.Spark
{
    public class DynamicElementInvoker
    {
        private object _dynamicElement = null;
        private string _code = null;
        private string _errors = null;
        private object[] _parameters = null;

        public string Code { get { return _code; } }
        public string Errors { get { return _errors; } }
        public object DynamicElement { get { return _dynamicElement; } }
        public object[] Parameters
        {
            get { return _parameters; }
            set { _parameters = value; }
        }

        public DynamicElementInvoker(string code, params object[] parameters)
        {
            //build code
            if (code == null)
                throw new Exception("Cannot create dynamic element because the code is null!");

            _parameters = parameters;

            _code = @"using System;
            namespace Symplectic.DynamicSpace {
                public class DynamicClass {
                    public object DynamicCode(params object[] parameters) {
            " + code + @"
                    }
                }
            }";
            //add compiler parameters
            CompilerParameters compilerParams = new CompilerParameters();
            compilerParams.CompilerOptions = "/target:library /optimize+";

            compilerParams.GenerateExecutable = false;
            compilerParams.GenerateInMemory = true;
            compilerParams.IncludeDebugInformation = false;

            // add some basic references
            compilerParams.ReferencedAssemblies.Add("System.dll");
            //compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
            //compilerParams.ReferencedAssemblies.Add("System.Data.dll");
            //compilerParams.ReferencedAssemblies.Add("System.Xml.dll");

            //compile the code
            CompilerResults compiled = CodeDomProvider.CreateProvider("c#").CompileAssemblyFromSource(
                    compilerParams,
                    _code);

            if (!compiled.Errors.HasErrors)
                _dynamicElement = compiled.CompiledAssembly.CreateInstance("Symplectic.DynamicSpace.DynamicClass");
            else
            {
                _dynamicElement = null;
                _errors = compiled.Errors.Count.ToString() + " Errors:";

                for (int x = 0; x < compiled.Errors.Count; x++)
                    _errors += "\r\nLine: " +
                                 compiled.Errors[x].Line.ToString() + " - " +
                                 compiled.Errors[x].ErrorText +
                                 " (code " + compiled.Errors[x].ErrorNumber + ")";

                _errors += "\r\n\r\n" + _code + "\r\n\r\n";
                foreach (string s in compiled.Output)
                    _errors += "\r\n" + s;

                throw new Exception("Failed to compile code\r\n" + _errors);
            }
        }


        public string Invoke()
        {
            if (_dynamicElement != null)
            {
                try
                {
                    object result = _dynamicElement.GetType().InvokeMember(
                        "DynamicCode", BindingFlags.InvokeMethod,
                        null, _dynamicElement, _parameters);
                    return result.ToString();
                }
                catch (Exception ex)
                {
                    throw new Exception("Error when invoking dynamic code: " + _code, ex);
                }
            }
            else
                return null;
        }
    }
}